package com.sskj.ffmpeg;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: Like
 * @Date: 2024/12/20 14:15
 * @Description:
 */
@Component
@Slf4j
public class RtspConvertHls {

    /**
     * 存储转码任务
     */
    private static final ConcurrentHashMap<String, RtspConvertHls.CoverThread> coverMap = new ConcurrentHashMap<>();
    /**
     * ffmpeg 路径
     */
    @Value("${ffmpeg.ffmpegPath}")
    private String ffmpegPath;

    /**
     * 文件输出根路径
     */
    @Value("${ffmpeg.rootOutputPath}")
    private String rootOutputPath;

    /**
     * 开启转换
     */
    public String startTransform(String code, String rtspUrl) {
        String hlsFileOutputPath = buildHlsFileOutputPath(code);
        String[] command = buildFfmpegCommand(rtspUrl, hlsFileOutputPath);

//        String hlsFileOutputPath = rootOutputPath + "\\"  + code + "\\" + code + ".m3u8";
//        String[] command = {
//                ffmpegPath,
//                "-i", rtspUrl,
//                "-c:v", "libx264",
//                "-preset", "fast",
//                "-crf", "20",
//                "-c:a", "aac",
//                "-b:a", "128k",
//                "-f", "hls",
//                "-hls_time", "10",
//                "-hls_list_size", "10",
//                "-hls_flags", "delete_segments",
//                hlsFileOutputPath
//        };

        String filePath = "/" + code + "/" + code + ".m3u8";
        String memKey = "startLive" + code;
        synchronized (memKey.intern()) {
            if (coverMap != null && coverMap.containsKey(code)) {
                stopTransform(code);
            }
            RtspConvertHls.CoverThread thread = new RtspConvertHls.CoverThread(code, rtspUrl, hlsFileOutputPath, command);
            coverMap.put(code, thread);
            thread.start();
        }
        return filePath;
    }

    private String buildHlsFileOutputPath(String code) {
        return rootOutputPath + "\\" + code + "\\" + code + ".m3u8";
    }

    private String[] buildFfmpegCommand(String rtspUrl, String hlsFileOutputPath) {
        return new String[]{
                ffmpegPath,
                "-i", rtspUrl,
                "-c:v", "libx264",
                "-preset", "fast",
                "-crf", "20",
                "-c:a", "aac",
                "-b:a", "128k",
                "-f", "hls",
                "-hls_time", "10",
                "-hls_list_size", "10",
                "-hls_flags", "delete_segments",
                hlsFileOutputPath
        };
    }

    /**
     * 停止转换
     */
    public void stopTransform(String code) {
        String memKey = "startLive" + code;
        synchronized (memKey.intern()) {
            if (coverMap.containsKey(code)) {
                RtspConvertHls.CoverThread thread = coverMap.get(code);
                if (thread != null && thread.getTaskState() != RtspConvertHls.CoverThread.fail) {
                    thread.stopConvert();
                    log.info("结束{}转换线程", code);
                }
            }
        }
    }


    /**
     * 执行命令线程
     */
    private class CoverThread extends Thread {

        //摄像机编号
        private String code;

        //rtsp地址
        private String rtspUrl;

        //hls文件生成地址
        private String hlsFileOutputPath;

        //输出文件名
        private String outputName;

        //命令
        private String[] command;

        //运行状态 0未开始 1进行中 -1失败
        private int taskState = 0;
        private static final int notStart = 0;
        private static final int running = 1;
        private static final int fail = -1;
        private Process process = null;

        CoverThread(String code, String rtspUrl, String hlsFileOutputPath, String[] command) {
            this.code = code;
            this.rtspUrl = rtspUrl;
            this.command = command;
            this.hlsFileOutputPath = hlsFileOutputPath;
            setName("m3u8-" + code);
            this.taskState = notStart;
        }

        @Override
        public void run() {
            try {
                outputName = rootOutputPath + code + "\\";

                this.checkFilePath();
                // 创建 ProcessBuilder 实例
                ProcessBuilder processBuilder = new ProcessBuilder(command);
                processBuilder.redirectErrorStream(true);
                //状态设置为运行中
                this.taskState = running;

                // 启动进程
                process = processBuilder.start();

                String line = "";
                File dir = new File(hlsFileOutputPath);
                int j = 0;
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    // 生成文件后返回视频路径
                    while (line != null || dir.exists()) {
                        line = reader.readLine();
                        dir = new File(hlsFileOutputPath);
                        log.info("line " + Integer.toString(j) + line);
                        log.info("diredire " + Integer.toString(j) + " " + dir.exists());
                        if (line == null || dir.exists()) {
                            break;
                        }
                        j++;
                    }

                    if (line != null) {
                        return;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

                // 等待进程结束
                int exitCode = process.waitFor();
                log.info("线程：{} ", getName() + " 退出码：" + exitCode);
                process = null;
                outputName = "";
            } catch (IOException | InterruptedException e) {
                log.error("出现异常" + e.getMessage(), e);
                this.taskState = fail;
            }
        }

        /**
         * 获取任务执行状态
         */
        public int getTaskState() {
            return taskState;
        }


        public String getRtspUrl() {
            return this.rtspUrl;
        }

        /**
         * 停止转码
         */
        public String stopConvert() {
            // 单进程
            String result = "false";
            if (process != null) {
                process.destroyForcibly();
                log.info("FFmpeg process stopped.");
                process = null;
                result = "true";
            }
            if (outputName != "") {
                this.delFileInPath();
                outputName = "";
            }
            return result;
        }

        /**
         * 删除文件夹下的文件
         */
        public void delFileInPath() {
            File directory = new File(outputName);

            // 检查目录是否存在并且是一个目录
            if (directory.exists() && directory.isDirectory()) {
                // 获取目录中的所有文件和子目录
                File[] files = directory.listFiles();

                if (files != null) {
                    for (File file : files) {
                        if (file.isFile()) {
                            if (outputName != "") {
                                if (file.getAbsolutePath().indexOf(outputName) != -1) {
                                    file.delete();
                                }
                            } else {
                                log.info("文件: " + file.getName());
                            }
                        } else if (file.isDirectory()) {
                            log.info("目录: " + file.getName());
                        }
                    }
                    directory.delete();
                } else {
                    log.info("无法列出目录中的文件");
                }
            } else {
                log.info("指定的路径不是一个有效的目录");
            }
        }

        /**
         * 判断创建文件夹
         */
        public void checkFilePath() {
            // 创建目录
            File dir = new File(outputName);
            boolean start = dir.exists();
            if (!dir.exists()) {
                dir.mkdirs();
                start = dir.exists();
            }
            log.info("创建目录outputName是否成功：{}", start);
        }

    }


}
